#ifndef AST_H
#define AST_H

#define YY_NO_UNPUT

using namespace std;

#include <iostream>
#include <stdio.h>
#include <string>
#include <list>

class Node
{
private:
  int             _line;

public:
  Node (int line = -1)
    : _line(line)
  {
  }


  int GetLine () const { return _line; }

  virtual void DebugInd (int ind) const
  {
    for (unsigned i=0; i<ind; i++)
      {
        printf ("  ");
      }
  }

  virtual void Debug (int ind = 0) const
  {
  }

  virtual void Reconstruct () const
  {
  }
};


class Identifier : public Node
{
protected:
  string  _name;
public:
  Identifier (string name, int line) : Node (line), _name (name) {  }
  string GetName () const { return _name; }
  void Reconstruct () const { printf (_name.c_str()); }
  void Debug (int ind = 0) const { DebugInd(ind); printf ("Identifier: %s\n", _name.c_str()); }
};

class ParenIdentifier : public Node
{
private:
  Node*  _identifier;

public:
  ParenIdentifier (Node* identifier) : Node(), _identifier(identifier) {}

  void Debug (int ind) const
  {
    DebugInd(ind); printf ("ParenIdentifier:\n");
    _identifier->Debug(ind+1);
  }

};

class DotIdentifier : public Node
{
protected:
  Node*  _identifier;
  Node*  _dotIdentifier;

public:

  DotIdentifier (Node *identifer, Node* dotIdentifier) : Node (), _identifier(identifer), _dotIdentifier (dotIdentifier) { }
  void Debug (int ind = 0) const
  {
    DebugInd(ind); printf ("DotIdentifier:\n");
    _identifier->Debug(ind+1);
    _dotIdentifier->Debug(ind+1);
  }
};


class ArrayIdentifier : public Node
{
protected:
  Node*  _identifier;
  Node*  _accessor;

public:

  ArrayIdentifier (Node *identifer, Node* accessor) : Node (), _identifier(identifer), _accessor(accessor) { }
  void Debug (int ind = 0) const
  {
    DebugInd(ind); printf ("ArrayIdentifier:\n");
    _identifier->Debug(ind+1);
    _accessor->Debug(ind+1);
  }
};



class ArrayAccessor : public Node
{
private:
  Node*  _expression;
  Node*  _follower;

public:
  ArrayAccessor (Node* expression, Node* follower) : Node (), _expression (expression), _follower (follower) { }

  void Debug (int ind) const
  {
    DebugInd(ind); printf ("ArrayAccessor:\n");
    _expression->Debug(ind+1);
    if (_follower)
      {
        _follower->Debug(ind+1);
      }
  }
};

class VariableDeclaration : public Node
{
private:
  Node* _type;
  Node* _name;

public:
  VariableDeclaration (Node* type, Node* name) : Node (), _type(type), _name(name) {}

  const Node* GetType () const { return _type; }
  const Node* GetName () const { return _name; }

  virtual void Reconstruct () const
  {
    _type->Reconstruct(); printf (" "); _name->Reconstruct(); Node::Reconstruct(); printf (";");
  }

  virtual void Debug (int ind) const
  {
    DebugInd(ind);printf ("VariableDeclaration:\n");
    _type->Debug(ind+1);
    _name->Debug(ind+1);

 }
};

class ParenExp : public Node
{
private:
  Node *_exp;
public:
  ParenExp (Node *exp) : Node (), _exp (exp) {}

  void Reconstruct()const {}
  void Debug (int ind) const
  {
    DebugInd(ind);
    printf ("ParenExp:\n");
    _exp->Debug(ind+1);
  }
};


class StatementList : public Node
{
private:
  list<Node*>  _nodes;

public:
  StatementList () : Node () { }

  void Add (Node* node) { _nodes.push_back(node); }

  void Debug (int ind) const
  {
    DebugInd(ind); printf ("StatementList:\n");

    for (list<Node*>::const_iterator it = _nodes.begin();
         it != _nodes.end();
         ++it)
      {
        Node* node = *it;
        node->Debug(ind+1);
      }
  }
};


class Statement : public Node
{
private:
  Node*  _exp;


public:
  Statement (Node* exp) : Node (), _exp(exp) { }

  void Debug(int ind) const
  {
    DebugInd(ind); printf ("Statement:\n");
    _exp->Debug(ind+1);
  }
};

class StatementBlock : public Node
{
private:
  list<Node*>  _nodes;

public:
  StatementBlock () : Node () { }

  void Add (Node* node) { _nodes.push_back(node); }

  void Debug (int ind) const
  {
    DebugInd(ind); printf ("StatementBlock:\n");

    for (list<Node*>::const_iterator it = _nodes.begin();
         it != _nodes.end();
         ++it)
      {
        Node* node = *it;
        node->Debug(ind+1);
      }
  }
};


class IfThenElse : public Node
{
private:
  Node *_cond;
  Node *_ifTrue;
  Node *_ifFalse;
public:
  IfThenElse (Node *cond, Node *ifTrue, Node* ifFalse) : Node (), _cond (cond), _ifTrue (ifTrue), _ifFalse (ifFalse) { }

  void Debug(int ind) const
  {
    DebugInd(ind); printf ("IfThenElse:\n");
    _cond->Debug(ind+1);
    _ifTrue->Debug(ind+1);
    if (_ifFalse)
      {
        _ifFalse->Debug(ind+1);
      }
  }
};

class UnOp : public Node
{
private:
  string  _op;
  Node *_child;
public:
  UnOp (string op, int line, Node *child) : Node (line), _op(op), _child(child) {}

  virtual void Reconstruct () const
  {
  }

  virtual void Debug (int ind) const
  {
    DebugInd (ind); printf ("UnOp: '%s'\n", _op.c_str());
    _child->Debug(ind+1);
  }
};


class BinOp : public Node
{
private:
  Node *_left;
  string  _op;
  Node *_right;
public:
  BinOp (Node *left, string op, int line, Node *right) : Node (line), _left (left), _op(op), _right(right) {}

  virtual void Reconstruct () const
  {
  }

  virtual void Debug (int ind) const
  {
    DebugInd (ind); printf ("BinOp: '%s'\n", _op.c_str());
    _left->Debug(ind+1);
    _right->Debug(ind+1);
  }
};


class TerOp : public Node
{
private:
  Node *_cond;
  Node *_ifTrue;
  Node *_ifFalse;
public:
  TerOp (Node *cond, Node *ifTrue, Node* ifFalse) : Node (), _cond (cond), _ifTrue (ifTrue), _ifFalse (ifFalse) { }

  void Debug(int ind) const
  {
    DebugInd(ind); printf ("Ternary:\n");
    _cond->Debug(ind+1);
    _ifTrue->Debug(ind+1);
    _ifFalse->Debug(ind+1);
  }
};

#endif // AST_H
